﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Neeko.XNA.UI
{
    public delegate void ZoomImageEvent(ZoomImage sender, GameTime gameTime);

    public class ZoomImage
    {
        public Vector2 Position { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }

        public bool CanZoom { get; private set; }
        public double Ratio { get; private set; }

        public event ZoomImageEvent OnClick;
        public event ZoomImageEvent OnMouseEnter;
        public event ZoomImageEvent OnMouseLeave;

        public Color Color { get; set; }

        Texture2D _texture;
        MouseState _previousMouseState;

        Rectangle _lastSourceRect;
        double _fadeProgress = 0;
        bool _fadeToZoom = false;
        const int FadeMiliseconds = 300;

        public ZoomImage(Texture2D texture, Vector2 position, int maxWidth, int maxHeight)
        {
            this.Color = Color.White;
            _texture = texture;
            this.Position = position;

            double realRatio = _texture.Width / (double)_texture.Height;
            double destinationRatio = maxWidth / (double)maxHeight;

            this.CanZoom = true;

            if (realRatio == destinationRatio)
            {
                this.Width = maxWidth;
                this.Height = maxHeight;

                if (_texture.Height < this.Height)
                    this.CanZoom = false;

                this.Ratio = _texture.Height / (double) this.Height;
            }
            else if (realRatio < destinationRatio)
            {
                this.Height = maxHeight;
                this.Width = (int)(realRatio * maxHeight);

                if (_texture.Height < this.Height)
                    this.CanZoom = false;

                this.Ratio = _texture.Height / (double)this.Height;
            }
            else
            {
                this.Width = maxWidth;
                this.Height = (int)(maxWidth / realRatio);

                if (_texture.Width < this.Width)
                    this.CanZoom = false;

                this.Ratio = _texture.Width / (double)this.Width;
            }

            _lastSourceRect = new Rectangle(0, 0, this.Width, this.Height);

            if (this.CanZoom)
            {
                this.OnMouseEnter += new ZoomImageEvent(ZoomImage_OnMouseEnter);
                this.OnMouseLeave += new ZoomImageEvent(ZoomImage_OnMouseLeave);
            }
        }

        public ZoomImage(Texture2D texture, Rectangle positionRectangle) : this(texture, new Vector2(positionRectangle.X, positionRectangle.Y), positionRectangle.Width, positionRectangle.Height) { }

        void ZoomImage_OnMouseLeave(ZoomImage sender, GameTime gameTime)
        {
            _fadeToZoom = false;
        }

        void ZoomImage_OnMouseEnter(ZoomImage sender, GameTime gameTime)
        {
            _fadeToZoom = true;
        }

        public void Update(GameTime gameTime)
        {
            if (this.CanZoom)
            {
                MouseState mouseState = Mouse.GetState();

                Rectangle thisRect = new Rectangle((int)this.Position.X, (int)this.Position.Y, this.Width, this.Height);
                Point mousePoint = new Point(mouseState.X, mouseState.Y);
                Point previousPoint = new Point(_previousMouseState.X, _previousMouseState.Y);


                if (thisRect.Contains(mousePoint))
                {
                    if (!thisRect.Contains(previousPoint))
                        if (OnMouseEnter != null)
                            OnMouseEnter(this, gameTime);

                    if (_previousMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
                        if (OnClick != null)
                            OnClick(this, gameTime);
                }
                else if (thisRect.Contains(previousPoint))
                {
                    if (OnMouseLeave != null)
                        OnMouseLeave(this, gameTime);
                }

                _previousMouseState = mouseState;

                if (_fadeToZoom)
                {
                    if (_fadeProgress < 1)
                    {
                        _fadeProgress += gameTime.ElapsedGameTime.TotalMilliseconds / (double)FadeMiliseconds;
                        if (_fadeProgress > 1)
                            _fadeProgress = 1;
                    }
                }
                else if (_fadeProgress > 0)
                {
                    _fadeProgress -= gameTime.ElapsedGameTime.TotalMilliseconds / (double)FadeMiliseconds;
                    if (_fadeProgress < 0)
                        _fadeProgress = 0;
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            Rectangle thisRectangle = new Rectangle((int)this.Position.X, (int)this.Position.Y, this.Width, this.Height);
            if (this.CanZoom)
            {
                Point mousePoint = new Point(_previousMouseState.X, _previousMouseState.Y);

                if (thisRectangle.Contains(mousePoint))
                {
                    double scale = _texture.Width / (double)this.Width;

                    int dX = mousePoint.X - thisRectangle.X;
                    int dY = mousePoint.Y - thisRectangle.Y;

                    double percentX = dX / (double)this.Width;
                    double percentY = dY / (double)this.Height;

                    int dWidth = _texture.Width - this.Width;
                    int dHeight = _texture.Height - this.Height;

                    int sourceX = (int)(dWidth * percentX);
                    int sourceY = (int)(dHeight * percentY);

                    _lastSourceRect = new Rectangle(sourceX, sourceY, this.Width, this.Height);
                }
            }
            spriteBatch.Draw(_texture, thisRectangle, this.Color);
            if (this.CanZoom)
            {
                Color fadeColor = new Color(255, 255, 255, (float)_fadeProgress);
                spriteBatch.Draw(_texture, thisRectangle, _lastSourceRect, fadeColor);
            }
        }
    }
}
